package com.hongji.lms5xxcommunicator.grid;

import java.util.*;

import com.hongji.lms5xxcommunicator.entity.ScanPointEntity;
import com.hongji.lms5xxcommunicator.grid.core.GridSpec;
import com.hongji.lms5xxcommunicator.grid.core.RasterState;
import com.hongji.lms5xxcommunicator.grid.core.UpdateMode;
import com.hongji.lms5xxcommunicator.grid.export.ExportService;
import com.hongji.lms5xxcommunicator.grid.export.GridDeltaPayload;
import com.hongji.lms5xxcommunicator.grid.export.GridPoint;
import com.hongji.lms5xxcommunicator.grid.update.MeanUpdateStrategy;
import com.hongji.lms5xxcommunicator.grid.update.ReplaceSingleElseMeanStrategy;
import com.hongji.lms5xxcommunicator.grid.update.ReplaceUpdateStrategy;
import com.hongji.lms5xxcommunicator.grid.update.UpdateStrategy;
import com.hongji.lms5xxcommunicator.grid.volume.VolumeParams;
import com.hongji.lms5xxcommunicator.grid.volume.VolumeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * RasterGrid（Facade 实现类）。
 */
public class RasterGrid implements RasterGridApi {

    private static final Logger log = LoggerFactory.getLogger(RasterGrid.class);

    // ========================= 状态 & 服务 =========================
    private final RasterState state;

    /** 更新策略：默认“严格替换”以满足实时改形需求。 */
    private UpdateStrategy strategy = new ReplaceUpdateStrategy();

    private final ExportService exportSvc;
    private final VolumeParams volumeParams = new VolumeParams();
    private final VolumeService volumeSvc;

    private static final double DEFAULT_METERS_PER_UNIT = 0.001;

    // ========================= 颜色范围追踪（给前端色标） =========================
    private double observedMinZ = Double.POSITIVE_INFINITY;
    private double observedMaxZ = Double.NEGATIVE_INFINITY;
    /** 每当全局 z 范围扩大时自增；前端据此触发一次性整幅重染色 */
    private long rangeSeq = 0L;

    /** 是否冻结 z 颜色范围（冻结后不再改 observedMinZ/MaxZ，也不再 rangeSeq++） */
    private boolean colorRangeFrozen = false;
    /** 冻结范围记录（可用于查询/日志） */
    private double frozenMinZ = Double.NaN, frozenMaxZ = Double.NaN;
    /** 去抖阈值（单位同 z；>0 时仅当超出该阈值才更新极值） */
    private double zRangeEps = 0.0;

    @Override
    public double getObservedMinZ() {
        return Double.isInfinite(observedMinZ) ? 0 : observedMinZ;
    }
    @Override
    public double getObservedMaxZ() {
        return Double.isInfinite(observedMaxZ) ? 0 : observedMaxZ;
    }
    @Override
    public long getRangeSeq() { return rangeSeq; }

    // ========================= 构造与配置 =========================
    public RasterGrid(GridSpec spec) {
        if (spec == null) throw new IllegalArgumentException("spec 不能为空");
        this.state = new RasterState(spec);
        this.exportSvc = new ExportService(state);
        this.volumeSvc = new VolumeService(state, volumeParams)
                .setMetersPerUnit(DEFAULT_METERS_PER_UNIT);
        log.info("初始化完成：rows={}, cols={}, dx={}, dy={}, metersPerUnit={}",
                spec.rows, spec.cols, spec.dx, spec.dy, DEFAULT_METERS_PER_UNIT);
    }

    public RasterGrid(GridSpec spec, double metersPerUnit) {
        if (spec == null) throw new IllegalArgumentException("spec 不能为空");
        this.state = new RasterState(spec);
        this.exportSvc = new ExportService(state);
        this.volumeSvc = new VolumeService(state, volumeParams)
                .setMetersPerUnit(metersPerUnit);
        log.info("初始化完成：rows={}, cols={}, dx={}, dy={}, metersPerUnit={}",
                spec.rows, spec.cols, spec.dx, spec.dy, metersPerUnit);
    }

    /** 非接口扩展：运行期改单位换算（可留用） */
    public RasterGrid setMetersPerUnit(double metersPerUnit) {
        this.volumeSvc.setMetersPerUnit(metersPerUnit);
        log.info("metersPerUnit 运行时更新：{}", metersPerUnit);
        return this;
    }

    /** ⭐ 对齐接口：体积参数配置 + 立即全量重算缓存 */
    @Override
    public void configureVolume(double baseline, boolean clampNegative) {
        volumeParams.setBaseline(baseline);
        volumeParams.setClampNegative(clampNegative);
        log.info("体积参数更新：baseline={}, clampNegative={}，开始全量重算", baseline, clampNegative);
        volumeSvc.recomputeAll();
        log.info("体积全量重算完成：volume={} m³", volumeSvc.get());
    }

    /** 设置更新策略（默认 REPLACE） */
    @Override
    public RasterGrid setUpdateMode(UpdateMode mode) {
        UpdateMode m = (mode == null) ? UpdateMode.REPLACE : mode;
        switch (m) {
            case REPLACE:
                this.strategy = new ReplaceUpdateStrategy();
                break;
            case REPLACE_SINGLE_ELSE_MEAN:
                this.strategy = new ReplaceSingleElseMeanStrategy();
                break;
            case MEAN:
            default:
                this.strategy = new MeanUpdateStrategy();
                break;
        }
        log.info("更新策略已设置：mode={}", m);
        return this;
    }

    @Override
    public double getVolume() {
        double v = volumeSvc.get();
        log.debug("读取体积：value={} m³", v);
        return v;
    }

    @Override
    public GridSpec spec() { return state.spec; }

    @Override
    public void clear() {
        state.clear();
        volumeSvc.invalidate();
        observedMinZ = Double.POSITIVE_INFINITY;
        observedMaxZ = Double.NEGATIVE_INFINITY;
        rangeSeq = 0L;
        log.info("网格已清空：体积缓存与范围已重置");
    }

    // ========================= 颜色范围控制（新增） =========================
    /** 设置极值去抖阈值（mm） */
    public RasterGrid setZRangeEps(double epsMm) {
        this.zRangeEps = Math.max(0.0, epsMm);
        return this;
    }

    /** 冻结颜色范围（单位同 z）；冻结后 merge 不再改 zMin/zMax 与 rangeSeq */
    public RasterGrid freezeColorRange(double minZ, double maxZ) {
        if (Double.isFinite(minZ) && Double.isFinite(maxZ) && maxZ > minZ) {
            this.observedMinZ = minZ;
            this.observedMaxZ = maxZ;
            this.frozenMinZ = minZ;
            this.frozenMaxZ = maxZ;
            this.colorRangeFrozen = true;
            log.info("颜色范围已冻结：zMin={} zMax={} (mm)", minZ, maxZ);
        } else {
            log.warn("freezeColorRange 忽略：非法范围 minZ={} maxZ={}", minZ, maxZ);
        }
        return this;
    }

    /** 解除冻结（恢复按数据扩展） */
    public RasterGrid unfreezeColorRange() {
        this.colorRangeFrozen = false;
        log.info("颜色范围冻结已解除；后续将按数据更新 zRange");
        return this;
    }

    public boolean isColorRangeFrozen() { return colorRangeFrozen; }

    // ========================= 增量更新（不含体积差分） =========================
    @Override
    public void mergePoints(List<ScanPointEntity> entities) {
        if (entities == null || entities.isEmpty()) {
            log.warn("增量更新（实体）：输入为空，忽略本次操作");
            return;
        }
        List<double[]> xyz = toXYZFromEntities(entities);
        mergeXYZ(xyz.get(0), xyz.get(1), xyz.get(2));
    }

    @Override
    public void mergeXYZ(double[] xs, double[] ys, double[] zs) {
        if (!validXYZ(xs, ys, zs)) {
            log.warn("增量更新（数组）：输入数组为空或长度不一致，忽略本次操作");
            return;
        }
        // 边界过滤
        List<double[]> filtered = filterPointsOutsideGrid(xs, ys, zs);
        if (filtered.get(0).length == 0) {
            log.warn("增量更新（数组）：所有点均超出网格范围，忽略本次操作");
            return;
        }

        double[] fx = filtered.get(0);
        double[] fy = filtered.get(1);
        double[] fz = filtered.get(2);

        // 对齐至格中心（你的设计）
        snapToGridCenterInPlace(fx, fy);

        // 写入（严格替换 / 或按策略）
        strategy.apply(state, fx, fy, fz);

        // 体积缓存失效
        volumeSvc.invalidate();

        // 扩大 z 范围则 bump 范围版本（供前端色标） —— 增加“冻结/去抖”控制
        double bMin = Double.POSITIVE_INFINITY, bMax = Double.NEGATIVE_INFINITY;
        for (double v : fz) {
            if (v < bMin) bMin = v;
            if (v > bMax) bMax = v;
        }

        if (!colorRangeFrozen) {
            boolean bump = false;
            if (bMin < observedMinZ - zRangeEps) { observedMinZ = bMin; bump = true; }
            if (bMax > observedMaxZ + zRangeEps) { observedMaxZ = bMax; bump = true; }
            if (bump) {
                rangeSeq++;
                log.debug("zRange 扩大：[{},{}) -> [{},{}), eps={}",
                        getObservedMinZ(), getObservedMaxZ(), observedMinZ, observedMaxZ, zRangeEps);
            }
        } else {
            // 冻结时不更新 zRange / rangeSeq；必要时可在此统计超界比率用于告警
        }

        log.info("增量更新（数组）完成：输入点数={}, 有效点数={}，体积缓存已失效；zRange=[{}, {}], rangeSeq={}, frozen={}, eps={}",
                xs.length, fx.length, getObservedMinZ(), getObservedMaxZ(), rangeSeq, colorRangeFrozen, zRangeEps);
    }

    // ========================= 体积差分 + 增量返回 =========================
    @Override
    public GridDeltaPayload mergeWithDelta(List<ScanPointEntity> entities, Integer minCountFilter) {
        if (entities == null || entities.isEmpty()) {
            log.warn("增量+体积差分：输入为空，返回空增量");
            return new GridDeltaPayload(Collections.<GridPoint>emptyList(), volumeSvc.get(), /*full*/false);
        }

        // 过滤越界
        List<ScanPointEntity> filteredEntities = new ArrayList<>();
        GridSpec sp = state.spec;
        for (ScanPointEntity e : entities) {
            if (e != null && e.getX() != null && e.getY() != null &&
                    isPointInGrid(e.getX(), e.getY())) {
                filteredEntities.add(e);
            } else {
                log.info("出现越界点，点位信息：{}，参数sp：{}", e ,sp.toString());
            }
        }
        if (filteredEntities.isEmpty()) {
            log.warn("增量+体积差分：所有点均超出网格范围，返回空增量");
            return new GridDeltaPayload(Collections.<GridPoint>emptyList(), volumeSvc.get(), /*full*/false);
        }

        // 触达格
        LinkedHashSet<Long> touched = collectTouched(filteredEntities);

        // 合并前贡献
        double before = volumeSvc.sumContributionForKeys(touched);

        // 执行合并（会失效体积缓存并更新 zRange/rangeSeq）
        List<double[]> xyz = toXYZFromEntities(filteredEntities);
        mergeXYZ(xyz.get(0), xyz.get(1), xyz.get(2));

        // 合并后贡献 & 累加差分
        double after = volumeSvc.sumContributionForKeys(touched);
        volumeSvc.addDelta(before, after);

        // 导出受影响格
        List<GridPoint> delta = exportSvc.fromKeys(touched, minCountFilter);
        GridDeltaPayload payload = new GridDeltaPayload(delta, volumeSvc.get(), /*full*/false);
        log.info("增量+体积差分：updates={} volume={} m³", delta.size(), payload.getVolume());
        return payload;
    }

    // ========================= 内部辅助 =========================
    private static long cellKey(int r, int c) {
        return (((long) r) << 32) ^ (c & 0xffffffffL);
    }

    private LinkedHashSet<Long> collectTouched(List<ScanPointEntity> entities) {
        LinkedHashSet<Long> touched = new LinkedHashSet<Long>();
        if (entities == null || entities.isEmpty()) return touched;
        for (ScanPointEntity e : entities) {
            if (e == null || e.getX() == null || e.getY() == null || e.getZ() == null) continue;
            int r = state.spec.toRow(e.getY());
            int c = state.spec.toCol(e.getX());
            if (state.inBounds(r, c)) touched.add(cellKey(r, c));
        }
        return touched;
    }

    public static List<double[]> toXYZFromEntities(List<ScanPointEntity> entities) {
        int cap = (entities == null) ? 0 : entities.size();
        double[] xs = new double[cap];
        double[] ys = new double[cap];
        double[] zs = new double[cap];
        int n = 0;
        if (entities != null) {
            for (ScanPointEntity e : entities) {
                if (e == null) continue;
                Double x = e.getX(), y = e.getY(), z = e.getZ();
                if (x == null || y == null || z == null) continue;
                xs[n] = x; ys[n] = y; zs[n] = z; n++;
            }
        }
        if (n < cap) {
            xs = Arrays.copyOf(xs, n);
            ys = Arrays.copyOf(ys, n);
            zs = Arrays.copyOf(zs, n);
        }
        List<double[]> out = new ArrayList<double[]>(3);
        out.add(xs); out.add(ys); out.add(zs);
        return out;
    }

    private void snapToGridCenterInPlace(double[] xs, double[] ys) {
        final GridSpec sp = state.spec;
        for (int i = 0; i < xs.length; i++) {
            int c = sp.toCol(xs[i]);
            int r = sp.toRow(ys[i]);
            xs[i] = sp.x0 + (c + 0.5) * sp.dx;
            ys[i] = sp.y0 + (r + 0.5) * sp.dy;
        }
        log.debug("坐标已对齐到格中心：count={}", xs.length);
    }

    private static boolean validXYZ(double[] xs, double[] ys, double[] zs) {
        return xs != null && ys != null && zs != null
                && xs.length == ys.length && xs.length == zs.length
                && xs.length > 0;
    }

    private List<double[]> filterPointsOutsideGrid(double[] xs, double[] ys, double[] zs) {
        List<Double> fx = new ArrayList<>(), fy = new ArrayList<>(), fz = new ArrayList<>();
        for (int i = 0; i < xs.length; i++) {
            if (isPointInGrid(xs[i], ys[i])) {
                fx.add(xs[i]); fy.add(ys[i]); fz.add(zs[i]);
            }
        }
        double[] ax = new double[fx.size()], ay = new double[fy.size()], az = new double[fz.size()];
        for (int i = 0; i < fx.size(); i++) { ax[i] = fx.get(i); ay[i] = fy.get(i); az[i] = fz.get(i); }
        List<double[]> res = new ArrayList<>(3);
        res.add(ax); res.add(ay); res.add(az);
        return res;
    }

    private boolean isPointInGrid(double x, double y) {
        final GridSpec sp = state.spec;
        return x >= sp.x0 && x < sp.x1 &&
                y >= sp.y0 && y < sp.y1;
    }

    /** 额外导出能力（非接口方法） */
    public List<GridPoint>
    exportGridPoints(boolean onlyHasData, int stride, Integer minCount) {
        return this.exportSvc.exportGridPoints(onlyHasData, stride, minCount);
    }
}
